using TMPro;
using UnityEngine;
using UnityEngine.Assertions;

namespace TMPAnimationLib
{
    public class CharObject
    {
        /// <summary>
        /// 字符内容
        /// </summary>
        public char Content;
        /// <summary>
        /// 是否为空格或换行符
        /// </summary>
        public bool IsEmpty { get; private set; }
        /// <summary>
        /// 字符所在索引
        /// </summary>
        public int Index { get; private set; }

        /// <summary>
        /// 字符动画状态
        /// </summary>
        public StateType AnimState { get; private set; }
        
        
        /// <summary>
        /// Mesh顶点（左下、左上、右上、右下）
        /// </summary>
        Vector3[] mVertices;
        Vector3[] mCopyVertices;
        AnimConfig mConfig;
        TextMeshProUGUI mText;
        int mMatIndex;
        int mBeginVertexIndex;
        float mTotalTime;
        float mCurTime;
        bool mNeedUpdateGeometry;
        bool mNeedUpdateColor;


        public CharObject()
        {
            mVertices = new Vector3[4];
            mCopyVertices = new Vector3[4];
        }

        /// <summary>
        /// 原所有字符的顶点总数
        /// </summary>
        int mAllCharVertexCountSource;
        /// <summary>
        /// 单个顶点额外新增顶点数
        /// </summary>
        int mPerVertexExtraVertexCount;
        
        public void SetData(TextMeshProUGUI text, int index, IAnimConfig config, int extraVertexCount)
        {
            mText = text;
            Index = index;
            mConfig = (AnimConfig)config;
            mPerVertexExtraVertexCount = extraVertexCount;
            
            Content = text.textInfo.characterInfo[Index].character;
            IsEmpty = Content.ToString() == " " || Content == '\n';
            AnimState = StateType.Wait;
            
            TMP_CharacterInfo charInfo = text.textInfo.characterInfo[index];
            mMatIndex = charInfo.materialReferenceIndex;
            mBeginVertexIndex = charInfo.vertexIndex;
            TMP_MeshInfo meshInfo = text.textInfo.meshInfo[mMatIndex];
            int vertexCount = (mPerVertexExtraVertexCount + 1) * 4; //字符顶点数
            mAllCharVertexCountSource = GetCharacterCountWithoutSpace() * 4; //原所有字符顶点总数

            if (mVertices.Length != vertexCount)
            {
                mVertices = new Vector3[vertexCount];
                mCopyVertices = new Vector3[vertexCount];
            }

            for (int i = 0, ii = 0; i < mPerVertexExtraVertexCount + 1; i++)
            {
                for (int j = 0; j < 4; j++, ii++)
                {
                    int vertexIndex = i * mAllCharVertexCountSource + j + mBeginVertexIndex;
                    mVertices[ii] = meshInfo.vertices[vertexIndex];
                    mCopyVertices[ii] = mVertices[ii];
                }
            }
            
            // for (int i = 0; i < vertexCount; i++)
            // {
            //     mVertices[i] = meshInfo.vertices[GetVertexIndex(i)];
            //     mCopyVertices[i] = mVertices[i];
            // }

            InitData();
        }
        
        void InitData()
        {
            if (IsJump()) return;
            mTotalTime = mConfig.SingleCharAnimTime;
            mCurTime = 0f;
            InitData_Delay();
            InitData_Move();
            InitData_Rotate();
            InitData_Scale();
            InitData_Color();
        }
        
        public void Reset()
        {
            if (IsJump()) return;
            mCurTime = 0f;
            ResetData_Delay();
            ResetData_Move();
            ResetData_Rotate();
            ResetData_Scale();
            ResetData_Color();
            AnimState = StateType.Wait;
        }

        public void Start()
        {
            Assert.IsTrue(AnimState != StateType.None);
            if (AnimState == StateType.Running) return;
            if (AnimState == StateType.Completed)
                Reset();
            if (IsJump())
            {
                AnimState = StateType.Completed;
            }
            else
            {
                //start
                AnimState = StateType.Running;
                
                if (mConfig.IsHideCharBeforeAnimBegin && mIsDelay)
                    Hide();
                else
                    Show();
            }
        }

        public void Stop()
        {
            Assert.IsTrue(AnimState != StateType.None);
            if (IsJump())
            {
                AnimState = StateType.Completed;
            }
            else
            {
                if (AnimState == StateType.Completed) return;
                //stop
                AnimState = StateType.Completed;

                if (mConfig.IsHideCharAfterAnimEnd || mConfig.IsHideCharAfterAllAnimEnd || mConfig.IsHideCharBeforeAnimBegin)
                    Hide();
                else
                    Show();
            }
        }
        
        public StateType OnUpdate(float deltaTime)
        {
            if (AnimState == StateType.Completed) return AnimState;
            
            if (IsJump())
            {
                AnimState = StateType.Completed;
                return AnimState;
            }
            
            Process_Delay(deltaTime);
            if (mIsDelay)
            {
                AnimState = StateType.Wait;
                return AnimState;
            }
            
            if (mCurTime >= mTotalTime)
            {
                AnimState = StateType.Completed;
                OnAnimCompleted();
                return AnimState;
            }

            AnimState = StateType.Running;
            mCurTime += deltaTime;
            float t = mCurTime / mTotalTime;
            if (t > 1f)
                t = 1f;
            Process_Move(t);
            Process_Rotate(t);
            Process_Scale(t);
            //Process_Color(t); //屏蔽颜色变化功能
            
            UpdateVertices();
            UpdateGeometry(false);
            
            ProcessUpdateGeometry();
            ProcessUpdateColor();
            return AnimState;
        }

        /// <summary>
        /// 显示字符
        /// </summary>
        public void Show()
        {
            // var vertexColor = (Color)mText.textInfo.meshInfo[mMatIndex].colors32[GetVertexIndex(0)];
            // vertexColor = new Color(vertexColor.r, vertexColor.g, vertexColor.b, 1f);
            UpdateColor(1f, true);
        }

        /// <summary>
        /// 隐藏字符
        /// </summary>
        public void Hide()
        {
            // var vertexColor = (Color)mText.textInfo.meshInfo[mMatIndex].colors32[GetVertexIndex(0)];
            // vertexColor = new Color(vertexColor.r, vertexColor.g, vertexColor.b, 0f);
            UpdateColor(0f, true);
        }

        void OnAnimCompleted()
        {
            if (mConfig.IsHideCharAfterAnimEnd)
                Hide();
        }

        
        #region Delay

        bool mIsDelay;
        float mDelayTime;
        void InitData_Delay()
        {
            mDelayTime = CalculateDelayTime();
            if (mDelayTime > 0f)
                mIsDelay = true;
            else
            {
                mIsDelay = false;
                OnDelayCompleted();
            }
        }
        void ResetData_Delay()
        {
            InitData_Delay();
        }
        void Process_Delay(float deltaTime)
        {
            if (!mIsDelay) return;
            mDelayTime -= deltaTime;
            if (mDelayTime <= 0f)
            {
                mIsDelay = false;
                OnDelayCompleted();
            }
        }

        void OnDelayCompleted()
        {
            if (mConfig.IsHideCharBeforeAnimBegin)
                Show();
        }
        
        float CalculateDelayTime()
        {
            float delay;
            if (mConfig.AnimType == AnimConfig.TextAnimType.FixedInterval)
                delay = mConfig.IntervalTime * GetIndex();
            else if (mConfig.AnimType == AnimConfig.TextAnimType.FixedTotalTime)
                delay = (mConfig.TotalTime - mConfig.SingleCharAnimTime) * GetIndex() / Mathf.Max(0.0001f, GetCharacterCount() - 1);
            else
            {
                delay = -1f;
                Debug.LogError($"CalculateDelayTime error! 尚未定义的 TextAnimType : {mConfig.AnimType.ToString()}");
            }
            return delay;
        }

        #endregion

        
        #region Move

        Vector3 mBeginPos;
        Vector3 mEndPos;
        Vector3 mMoveOffset = Vector3.zero;
        Vector3 mCenterPos;
        Vector3 mCamberRotate; //弧形排列额外产生的旋转值
        void InitData_Move()
        {
            if (!mConfig.EnableMoveAnim) return;
            mCenterPos = GetCenterPos();
            mBeginPos = mCenterPos + mConfig.BeginPosOffset * GetBeginPosOffsetRatio();
            mEndPos = mCenterPos + mConfig.EndPosOffset;
            mMoveOffset = mBeginPos - mCenterPos;

            if (mConfig.EnableCamber && Mathf.Abs(mConfig.CamberAngle - 180f) > 0.001f && GetCharacterCountWithoutSpace() > 1)
            {
                var vertices = mText.textInfo.meshInfo[mMatIndex].vertices;
                int beginIndex = mAllCharVertexCountSource * mPerVertexExtraVertexCount; //字符第一个索引
                int endIndex = beginIndex + mAllCharVertexCountSource - 1; //字符最后一个索引
                var beginPos = (vertices[beginIndex] + vertices[beginIndex + 3]) * 0.5f; //bottom中点
                var endPos = (vertices[endIndex - 3] + vertices[endIndex]) * 0.5f;
                endPos = new Vector3(endPos.x, beginPos.y, endPos.z); //以第一个字符的bottom为基准
                var halfPos = (beginPos + endPos) * 0.5f;
                Vector3 oPos = new Vector3(halfPos.x, halfPos.y - (Mathf.Tan(mConfig.CamberAngle * 3.1415926f / 360f) * (endPos.x - halfPos.x)), halfPos.z); //圆心
                float t = (mCenterPos.x - beginPos.x) / (endPos.x - beginPos.x); //当前字符在所有字符中所在的比例
                var sPos = Vector3.Slerp((beginPos - oPos), (endPos - oPos), t) + oPos; //球面插值
                mEndPos = sPos + new Vector3(0, mCenterPos.y - beginPos.y, 0f); //从bottom反推到center

                float angle;
                if (oPos.x > mCenterPos.x)
                    angle = Mathf.Atan((oPos.x - mCenterPos.x)/(mCenterPos.y - oPos.y)) / 3.1415926f * 180f;
                else
                    angle = -Mathf.Atan((mCenterPos.x - oPos.x)/(mCenterPos.y - oPos.y)) / 3.1415926f * 180f;
                mCamberRotate = new Vector3(0, 0, angle);
            }
            else
                mCamberRotate = Vector3.zero;
        }
        void ResetData_Move()
        {
            mMoveOffset = mConfig.BeginPosOffset;
        }
        void Process_Move(float t)
        {
            if (!mConfig.EnableMoveAnim) return;
            float v = mConfig.MoveAnimCurve.Evaluate(t);
            Vector3 pos = Util.Lerp_Vec3(mBeginPos, mEndPos, v);
            mMoveOffset = pos - mCenterPos;
        }
        float GetBeginPosOffsetRatio()
        {
            float ratio = 1f;
            if (mConfig.EnableRandomBeginPosOffset)
                ratio = Random.Range(mConfig.BeginPosOffset_RandomRange.x, mConfig.BeginPosOffset_RandomRange.y);
            if (mConfig.EnableSequenceBeginPosOffset)
            {
                float t = 1f * (GetIndex() + 1) / GetCharacterCount();
                ratio = mConfig.SequenceBeginPosOffsetRatioMultiplier *
                        mConfig.SequenceBeginPosOffsetRatioCurve.Evaluate(t);
            }
            return ratio;
        }

        #endregion


        #region Rotate

        Vector3 mBeginRotate;
        Vector3 mEndRotate;
        Vector3 mCurRotate = Vector3.zero;
        void InitData_Rotate()
        {
            if (!mConfig.EnableRotateAnim) return;
            mBeginRotate = mConfig.BeginRotate * GetBeginRotateRatio();
            mEndRotate = mConfig.EndRotate + mCamberRotate;
            mCurRotate = mBeginRotate;
        }
        void ResetData_Rotate()
        {
            mCurRotate = mBeginRotate;
        }
        void Process_Rotate(float t)
        {
            if (!mConfig.EnableRotateAnim) return;
            float v = mConfig.RotateAnimCurve.Evaluate(t);
            mCurRotate = Util.Lerp_Vec3(mBeginRotate, mEndRotate, v);
        }
        float GetBeginRotateRatio()
        {
            float ratio = 1f;
            if (mConfig.EnableRandomBeginRotate)
                ratio = Random.Range(mConfig.BeginRotate_RandomRange.x, mConfig.BeginRotate_RandomRange.y);
            if (mConfig.EnableSequenceBeginRotate)
            {
                float t = 1f * (GetIndex() + 1) / GetCharacterCount();
                ratio = mConfig.SequenceBeginRotateRatioMultiplier *
                        mConfig.SequenceBeginRotateRatioCurve.Evaluate(t);
            }
            return ratio;
        }
        
        #endregion


        #region Scale

        Vector3 mBeginScale;
        Vector3 mEndScale;
        Vector3 mCurScale = Vector3.one;
        void InitData_Scale()
        {
            if (!mConfig.EnableScaleAnim) return;
            mBeginScale = mConfig.BeginScale * GetBeginScaleRatio();
            mEndScale = mConfig.EndScale;
            mCurScale = mBeginScale;
        }
        void ResetData_Scale()
        {
            mCurScale = mBeginScale;
        }
        void Process_Scale(float t)
        {
            if (!mConfig.EnableScaleAnim) return;
            float v = mConfig.ScaleAnimCurve.Evaluate(t);
            mCurScale = Util.Lerp_Vec3(mBeginScale, mEndScale, v);
        }
        float GetBeginScaleRatio()
        {
            float ratio = 1f;
            if (mConfig.EnableRandomBeginScale)
                ratio = Random.Range(mConfig.BeginScale_RandomRange.x, mConfig.BeginScale_RandomRange.y);
            if (mConfig.EnableSequenceBeginScale)
            {
                float t = 1f * (GetIndex() + 1) / GetCharacterCount();
                ratio = mConfig.SequenceBeginScaleRatioMultiplier *
                        mConfig.SequenceBeginScaleRatioCurve.Evaluate(t);
            }
            return ratio;
        }
        
        #endregion
        
        
        #region Color

        void InitData_Color()
        {
            if (!mConfig.EnableColorAnim) return;
        }
        void ResetData_Color()
        {
        }
        void Process_Color(float t)
        {
            if (!mConfig.EnableColorAnim) return;
            Color color = mConfig.gradient.Evaluate(t);
            UpdateColor(color, false);
        }

        #endregion
        
        
        int GetCharacterCount()
        {
            if (mConfig.IsJumpEmptyCharAnim)
                return GetCharacterCountWithoutSpace();
            else
                return mText.textInfo.characterCount;
        }

        int GetCharacterCountWithoutSpace()
        {
            int cnt = 0;
            //mText.textInfo.characterInfo在文本变化后更新不及时
            // foreach (var item in mText.textInfo.characterInfo)
            // {
            //     if (item.character == ' ' || item.character == '\n') continue;
            //     cnt++;
            // }
            foreach (var item in mText.text)
            {
                if (item == ' ' || item == '\n') continue;
                cnt++;
            }
            return Mathf.Min(mText.textInfo.characterCount, cnt);
        }

        int GetIndex()
        {
            if (mConfig.IsJumpEmptyCharAnim)
            {
                var charInfoArray = mText.textInfo.characterInfo;
                int spaceCount = 0;
                for (int i = 0; i < Index; i++)
                {
                    if (charInfoArray[i].character.ToString() == " ")
                        spaceCount++;
                }
                int index = Index - spaceCount;
                return index;
            }
            else
            {
                return Index;
            }
        }
        
        Vector3 GetCenterPos()
        {
            var pos = (mVertices[mVertices.Length - 3] + mVertices[mVertices.Length - 1]) * 0.5f;
            return pos;
        }
        
        int GetVertexIndex(int index)
        {
            return mBeginVertexIndex + index + mAllCharVertexCountSource * mPerVertexExtraVertexCount;
        }
        
        int VertexIndexToAllVertexIndex(int vertexIndex)
        {
            int x = vertexIndex / 4;
            int y = vertexIndex % 4;
            int index = x * mAllCharVertexCountSource + mBeginVertexIndex + y;
            return index;
        }
        
        bool IsJump()
        {
            return IsEmpty;
        }

        
        #region Vertex Update

        /// <summary>
        /// 更新顶点数据
        /// </summary>
        void UpdateVertices()
        {
            //位移旋转缩放矩阵
            Matrix4x4 matrix = Matrix4x4.TRS(mMoveOffset, Quaternion.Euler(mCurRotate), mCurScale);
            for (int i = 0; i < mVertices.Length; i++)
            {
                var vertex = mCopyVertices[i] - mCenterPos;
                vertex = matrix.MultiplyPoint3x4(vertex) + mCenterPos;
                mVertices[i] = vertex;
            }
        }

        /// <summary>
        /// 请求更新 TMP Geometry
        /// </summary>
        /// <param name="force">是否强制更新</param>
        void UpdateGeometry(bool force)
        {
            mNeedUpdateGeometry = true;
            if (force)
                ProcessUpdateGeometry();
        }

        /// <summary>
        /// 更新 TMP Geometry
        /// </summary>
        void ProcessUpdateGeometry()
        {
            if (mNeedUpdateGeometry)
            {
                var textInfo = mText.textInfo;
                var vertices = textInfo.meshInfo[mMatIndex].vertices;
                for (int i = 0; i < mVertices.Length; i++)
                    vertices[VertexIndexToAllVertexIndex(i)] = mVertices[i];
                // for (int i = 0; i < 4; i++)
                //     vertices[GetVertexIndex(i)] = mVertices[i];
                textInfo.meshInfo[mMatIndex].mesh.vertices = vertices;
                mText.UpdateGeometry(textInfo.meshInfo[mMatIndex].mesh, mMatIndex);
                mNeedUpdateGeometry = false;
            }
        }

        #endregion


        #region Color Update

        /// <summary>
        /// 请求更新顶点颜色
        /// </summary>
        /// <param name="color"></param>
        /// <param name="force">是否强制更新</param>
        void UpdateColor(Color32 color, bool force)
        {
            var colors = mText.textInfo.meshInfo[mMatIndex].colors32;
            for (int i = 0; i < mVertices.Length; i++)
                colors[VertexIndexToAllVertexIndex(i)] = color;
            // for (int i = 0; i < 4; i++)
            //     mText.textInfo.meshInfo[mMatIndex].colors32[GetVertexIndex(i)] = color;
            mNeedUpdateColor = true;
            if (force)
                ProcessUpdateColor();
        }
        
        /// <summary>
        /// 请求更新顶点颜色
        /// </summary>
        /// <param name="alpha">透明度</param>
        /// <param name="force">是否强制更新</param>
        void UpdateColor(float alpha, bool force)
        {
            var colors = mText.textInfo.meshInfo[mMatIndex].colors32;
            for (int i = 0; i < mVertices.Length; i++)
            {
                int index = VertexIndexToAllVertexIndex(i);
                var color = (Color)colors[index];
                colors[index] = (Color32)new Color(color.r, color.g, color.b, alpha);
            }
            mNeedUpdateColor = true;
            if (force)
                ProcessUpdateColor();
        }
        
        /// <summary>
        /// 更新 TMP 顶点颜色
        /// </summary>
        void ProcessUpdateColor()
        {
            if (mNeedUpdateColor)
            {
                mText.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
                mNeedUpdateColor = false;
            }
        }

        #endregion
    }
}